import Taro from "@tarojs/taro";
import { Ref, ref, unref } from "vue";
import { castArray, isNil } from "lodash";

import useFetch, { TUseFetchOptions } from "./useFetch";

const isEmpty = (value) =>
  (Array.isArray(value) && value.length == 0) || isNil(value) || value === "";

export function formValidateField(value, rules) {
  if (!rules || rules.length == 0) return undefined;
  return rules.find((rule) => {
    return (
      (rule.required && isEmpty(value)) ||
      (rule.pattern && !rule?.pattern.test(value)) ||
      (rule.validator && rule.validator(value, rule))
    );
  });
}

export function formValidate(model, rules: TRule, keys?: string[]) {
  return new Promise((resolve, reject) => {
    const error: any[] = [];
    if (rules) {
      keys = !keys || keys?.length == 0 ? Object.keys(rules) : keys;
      for (let key of keys) {
        let fieldRules = castArray(rules[key]);
        const value = Reflect.get(model, key);
        const err = formValidateField(value, fieldRules);
        if (err) error.push(err);
      }
    }
    error.length > 0 ? reject(error) : resolve({});
  });
}

type TRule = {
  required?: boolean;
  message?: string;
  pattern?: RegExp;
};

type UTRules = TRule | TRule[];

type TUseFormOptions<TModel> = TUseFetchOptions & {
  defaultModel?: Partial<TModel>;
  rules?: { [key: string]: UTRules } | Ref<{ [key: string]: UTRules }>;
};

const useForm = <TModel = Record<string, any>>(
  url: string,
  {
    defaultModel = {} as TModel,
    rules,
    ...restOptions
  }: TUseFormOptions<TModel> = {}
) => {
  const model = ref<TModel | typeof defaultModel>(
    defaultModel || ({} as typeof defaultModel)
  );

  const { run, ...rest } = useFetch(url, {
    ...restOptions,
    immediate: false,
  });

  const submit = () => {
    validate()
      .then(() => {
        run(model.value as Record<string, any>);
      })
      .catch((errs) => {
        Taro.showToast({
          icon: "none",
          title: errs[0]["message"] || "",
        });
      });
  };

  const validate = (keys?: string[]) =>
    formValidate(unref(model), unref(rules) as TRule, keys);

  return {
    ...rest,
    model,
    submit,
    validate,
  };
};

export default useForm;
